package com.zy.je.common.core.utils.math;

import com.zy.je.common.core.utils.collect.ListUtil;
import com.zy.je.common.core.utils.lang.NumberUtil;
import com.zy.je.common.core.utils.lang.ObjectUtil;
import com.zy.je.common.core.utils.lang.StringUtil;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;

/**
 * 流式统计工具--通流式操作记算最大值、最小值、和、数量、平均数、方差
 */
public final class NumberStatHelper implements Serializable {
    public static void main(String[] args) {
        List<Integer> valueList = ListUtil.newArrayList(1, 3, 7, 19, 20);
        //计算标准差
        NumberStatHelper statHelper = valueList.stream()
                .map(it -> ObjectUtil.toBigDecimal(it))
                .map(it -> new NumberStatHelper(it))
                .reduce(NumberStatHelper::add)
                .get();
        System.out.println(StringUtil.fmtWithParam("平均值：{}", statHelper.getMean()));
        System.out.println(StringUtil.fmtWithParam("最小值：{}", statHelper.getMin()));
        System.out.println(StringUtil.fmtWithParam("最大值：{}", statHelper.getMax()));
        System.out.println(StringUtil.fmtWithParam("计数：{}", statHelper.getCount()));
        System.out.println(StringUtil.fmtWithParam("标准差：{}", statHelper.getStandardDeviation()));
        System.out.println(StringUtil.fmtWithParam("方差：{}", statHelper.getVariance()));

        System.out.println(StringUtil.fmtWithParam("数据波动：{}",
                NumberStatHelper.calcFluctuation(ObjectUtil.toBigDecimal(valueList.get(0)), statHelper))
        );
    }

    static public BigDecimal calcFluctuation(BigDecimal data, NumberStatHelper statHelper) {
        BigDecimal fluctuation = BigDecimal.ZERO;
        BigDecimal mean = statHelper.getMean();
        BigDecimal standardDeviation = statHelper.getStandardDeviation();
        if (standardDeviation.compareTo(BigDecimal.ZERO) != 0) {
            fluctuation = NumberUtil.div(
                    NumberUtil.sub(data, mean),
                    standardDeviation
            );
        }
        return fluctuation;
    }

    private static final long serialVersionUID = 1L;
    private final BigDecimal x;
    private final BigDecimal xx;
    private final BigDecimal min;
    private final BigDecimal max;
    private final BigDecimal count;

    private NumberStatHelper(BigDecimal x, BigDecimal xx, BigDecimal min, BigDecimal max, BigDecimal count) {
        this.x = x;
        this.xx = xx;
        this.min = min;
        this.max = max;
        this.count = count;
    }

    public NumberStatHelper(BigDecimal x) {
        BigDecimal xx = NumberUtil.mul(x, x);
        this.x = x;
        this.xx = xx;
        this.min = x;
        this.max = x;
        this.count = BigDecimal.ONE;
    }

    //定义求“和”方法
    public NumberStatHelper add(NumberStatHelper that) {
        return new NumberStatHelper(
                NumberUtil.add(x, that.x),
                NumberUtil.add(xx, that.xx),
                NumberUtil.min(min, that.min),
                NumberUtil.max(max, that.max),
                NumberUtil.add(count, that.count));
    }

    /**
     * 和
     *
     * @return
     */
    public BigDecimal getSum() {
        return x;
    }

    /**
     * 平均值
     *
     * @return
     */
    public BigDecimal getMean() {
        return NumberUtil.div(x, count);
    }

    /**
     * 方差
     *
     * @return
     */
    public BigDecimal getVariance() {
        if (count.compareTo(BigDecimal.ONE) <= 0) {
            return BigDecimal.ZERO;
        }
        //xx / (count - 1) - x*x/(count*(count-1))
        return NumberUtil.sub(
                NumberUtil.div(xx, NumberUtil.sub(count, BigDecimal.ONE)),
                NumberUtil.div(NumberUtil.mul(x, x), NumberUtil.mul(count, NumberUtil.sub(count, BigDecimal.ONE)))
        );
    }

    /**
     * 标准差
     */
    public BigDecimal getStandardDeviation() {
        return BigRootUtil.bigRoot(getVariance(), 2, 10, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 最小值
     *
     * @return
     */
    public BigDecimal getMin() {
        return min;
    }

    /**
     * 最大值
     *
     * @return
     */
    public BigDecimal getMax() {
        return max;
    }

    /**
     * 计数
     *
     * @return
     */
    public BigDecimal getCount() {
        return count;
    }

    @Override
    public String toString() {
        return "Helper{"
                + "x="
                + x
                + ", xx="
                + xx
                + ", min="
                + min
                + ", max="
                + max
                + ", count="
                + count
                + '}';
    }
}